package com.wheesper.www.WheesperServer.Services;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import com.Wheesper.www.WheesperServer.Model.Group;
import com.Wheesper.www.WheesperServer.Model.Group.Member;
import com.Wheesper.www.WheesperServer.Model.GroupCenter;
import com.Wheesper.www.WheesperServer.Model.User;
import com.Wheesper.www.WheesperServer.Model.UserCenter;
import com.Wheesper.www.WheesperServer.Model.UserInfo;
import com.Wheesper.www.WheesperServer.Model.UserInfoCenter;
import com.wheesper.www.WheesperServer.Common.OfflineMessageTrusteeship;
import com.wheesper.www.WheesperServer.Net.Session.IOSession;
import com.wheesper.www.WheesperServer.Utils.Logger;

import ProtocolBuffer.ProtoMessageOuterClass.GroupCreateApplyResponse;
import ProtocolBuffer.ProtoMessageOuterClass.GroupInfo;
import ProtocolBuffer.ProtoMessageOuterClass.GroupListResponse;
import ProtocolBuffer.ProtoMessageOuterClass.GroupParticipateInApplyPushMessage;
import ProtocolBuffer.ProtoMessageOuterClass.GroupParticipateInApplyRequest;
import ProtocolBuffer.ProtoMessageOuterClass.GroupParticipateInApplyResponse;
import ProtocolBuffer.ProtoMessageOuterClass.GroupParticipateInReplyPushMessage;
import ProtocolBuffer.ProtoMessageOuterClass.GroupParticipateInReplyRequest;
import ProtocolBuffer.ProtoMessageOuterClass.GroupParticipateInReplyResponse;
import ProtocolBuffer.ProtoMessageOuterClass.GroupSearchResponse;
import ProtocolBuffer.ProtoMessageOuterClass.ProtoMessage;

public class GroupController {
	private static GroupController _instance = new GroupController();
	public static GroupController Instance() {
		return _instance;
	}
	private GroupController() {
		_applyer2routines = new HashMap<String,Set<GroupParticipateInRoutine>>();
	}
	private Map<String ,Set<GroupParticipateInRoutine>> _applyer2routines;
	private void groupCreateApplyResponse(IOSession session,Integer group_id,String creator_email,String group_name,String group_info) {
		GroupCreateApplyResponse response = GroupCreateApplyResponse.newBuilder()
				.setGroupCreatorEmail(creator_email)
				.setGroupId(group_id)
				.setGroupName(group_name)
				.setGroupIntro(group_info)
				.build();
		
		ProtoMessage message = ProtoMessage.newBuilder()
				.setGroupCreateApplyResponse(response)
				.build();
		
		session.writeToRemote(message.toByteArray());
	}
	
	private void groupParticipateInApplyResponse(IOSession session,Integer group_id,String applyer_email,boolean status) {
		GroupParticipateInApplyResponse response = GroupParticipateInApplyResponse.newBuilder()
				.setGroupId(group_id)
				.setApplyerEmail(applyer_email)
				.setStatus(status)
				.build();
		
		ProtoMessage message = ProtoMessage.newBuilder()
				.setGroupParticipateInApplyResponse(response)
				.build();
		
		session.writeToRemote(message.toByteArray());
	}
	
	public void groupParticipateInReplyResponse(IOSession session,
			Integer group_id,String group_name,String applyer_email,
			String applyer_nickname,boolean is_accepted) {
		GroupParticipateInReplyResponse response =GroupParticipateInReplyResponse.newBuilder()
				.setGroupId(group_id)
				.setGroupName(group_name)
				.setApplyerEmail(applyer_email)
				.setApplyerNickname(applyer_nickname)
				.setIsAccepted(is_accepted)
				.build();
		ProtoMessage message = ProtoMessage.newBuilder()
				.setGroupParticipateInReplyResponse(response)
				.build();
		
		session.writeToRemote(message.toByteArray());
	}
	
	
	private void groupParticipateInApplyPushMessage(IOSession session,User creator,GroupParticipateInApplyPushMessage message) {
		ProtoMessage m = ProtoMessage.newBuilder()
				.setGroupParticipateInApplyPushMessage(message)
				.build();
		//如果creator不在线，则自动转化为离线消息推送给群创建者
		if(session == null) {
			offlineMessagePush(creator,m);
			return;
		}
		session.writeToRemote(m.toByteArray());
	}
	
	private void groupParticipateInReplyPushMessage(IOSession session,User applyer,GroupParticipateInReplyPushMessage message) {
		ProtoMessage m = ProtoMessage.newBuilder()
				.setGroupParticipateInReplyPushMessage(message)
				.build();
		if(session == null) {
			offlineMessagePush(applyer,m);
			return;
		}
		session.writeToRemote(m.toByteArray());
	}
	
	//离线消息通过这个方法来推送
	private void offlineMessagePush(User user,ProtoMessage message) {
		OfflineMessageTrusteeship.Instance().trusteeFor(user, message);
	}
	
	public void creatingGroup(IOSession session,String creator_email,String group_name,String group_info) {
		
		//如果要对用户建群增加限制，可以在这里增加条件控制
		Group group =  GroupCenter.Instance().createGroup(creator_email, group_name, group_info);
		groupCreateApplyResponse(session,group.ID(),group.CreatorEmail(),group.Name(),group.Intro());
		Logger.Instance().logging("[Group Created Result]{\n"
				+ "GroupID:" + group.ID()+" \n"
				+ "GroupName:" + group.Name()+" \n"
				+ "GroupIntro:" + group.Intro()+" \n"
				+ "CreatorEmail:" + group.CreatorEmail()+" \n"
				+ "}");
	}
	
	public void applyingParticipateInGroup(IOSession session,GroupParticipateInApplyRequest request) {
		
		int group_id = request.getGourpId();
		String applyer_email = request.getApplyerEmail();
		//添加 加群申请业务
		GroupParticipateInRoutine routine = new GroupParticipateInRoutine(applyer_email,group_id);
		routine.NextStatus(request);
		
		
		Set<GroupParticipateInRoutine> routines = _applyer2routines.get(applyer_email);
		
		if(routines == null) {
			routines = new HashSet<GroupParticipateInRoutine>();
			routines.add(routine);
			_applyer2routines.put(applyer_email, routines);
		}else {
			routines.add(routine);
		}
		
		groupParticipateInApplyResponse(session,group_id,applyer_email,true);
		Logger.Instance().logging("[Applying Participate Routine]{\n"
				+ "ApplyerEmail:" + applyer_email + "\n"
				+ "GroupID:" + group_id + "\n}");
		
		//推送至群创建者的Client上
		Group group = GroupCenter.Instance().searchGroupByID(group_id);
		String creator_email = group.CreatorEmail();
		String group_name = group.Name();
		
		UserInfo creator_info = UserInfoCenter.Instance().UserInfo(creator_email);
		
		GroupParticipateInApplyPushMessage message = GroupParticipateInApplyPushMessage.newBuilder()
				.setApplyerEmail(applyer_email)
				.setGourpId(group_id)
				.setGroupName(group_name)
				.setApplyerNickname(creator_info.NickName())
				.build();
				
		User creator = UserCenter.Instance().UserObject(creator_email);
				
		
		IOSession creator_session = LogController.Instance().GetIOSessionBy(creator);
		groupParticipateInApplyPushMessage(creator_session,creator, message);
	}
	
	public void replyingParticipateInGroup(IOSession session,GroupParticipateInReplyRequest request) {
		Integer group_id = request.getGroupId();
		String applyer_email = request.getApplyerEmail();
		
		Set<GroupParticipateInRoutine> routines = _applyer2routines.get(applyer_email);
		Iterator<GroupParticipateInRoutine> iter = routines.iterator();
		
		groupParticipateInReplyResponse(session,group_id,request.getGroupName(),request.getApplyerEmail()
				,request.getApplyerNickname(),request.getIsAccepted());
		
		GroupParticipateInRoutine routine = null;
		while(iter.hasNext()) {
			routine = iter.next();
			if(routine.applyer().contentEquals(applyer_email) 
					&&routine.GroupID() == group_id ) {
				iter.remove();
				break;
			}
		}
		try {
			routine.NextStatus(request);
			handleRoutine(routine);
			
			GroupParticipateInReplyPushMessage message = GroupParticipateInReplyPushMessage.newBuilder()
					.setGroupId(group_id)
					.setGroupName(request.getGroupName())
					.setIsAccepted(request.getIsAccepted())
					.build();
			User applyer = UserCenter.Instance().UserObject(applyer_email);
			IOSession applyer_session = LogController.Instance().GetIOSessionBy(applyer);
			groupParticipateInReplyPushMessage(applyer_session,applyer,message);
		}catch(Exception e) {
			Logger.Instance().logging(e.toString());
		}
	}
	
	
	
	private void handleRoutine(GroupParticipateInRoutine routine) {
		Integer group_id = routine.GroupID();
		String applyer_email = routine.applyer();
		if(routine.status() == GroupParticipateInRoutine.Status.FINISHED_WITH_SUCCESS) {
			//加入群组成功
			Logger.Instance().logging("[Group Participate In Result]{\n"
					+ "GroupID:" + group_id + "\n"
					+ "ApplyerEmail:" + applyer_email + "\n"
					+ "Result:Accepted\n}");
			GroupCenter.Instance().userParticipateInGroup(applyer_email, group_id);
		}else {
			Logger.Instance().logging("[Group Participate In Result]{\n"
					+ "GroupID:" + group_id + "\n"
					+ "ApplyerEmail:" + applyer_email + "\n"
					+ "Result:Rejected\n}");
		}
	}
	
	
	private void groupSearchResponse(IOSession session,String group_name,Set<Group> groups) {
		GroupSearchResponse.Builder response_builder = GroupSearchResponse.newBuilder();
		response_builder.setGroupName(group_name);

		groups.forEach(new Consumer<Group>() {
			@Override
			public void accept(Group group) {
				GroupInfo.Builder info_builder = GroupInfo.newBuilder();
				
				info_builder.setCreatorEmail(group.CreatorEmail())
				.setGroupId(group.ID())
				.setGroupIntro(group.Intro())
				.setGroupMemberNumber(group.MemberNum());
				
				ArrayList<Group.Member> members = group.Members();
				members.forEach(new Consumer<Group.Member>() {

					@Override
					public void accept(Member member) {
						GroupInfo.Member mem = GroupInfo.Member.newBuilder()
								.setMemberEmail(member.Email())
								.setMemberNickname(member.Nickname())
								.build();
						info_builder.addMembers(mem);
					}
					
				});
				
				response_builder.addResults(info_builder.build());
			}
		});
		
		ProtoMessage message = ProtoMessage.newBuilder()
				.setGroupSearchResponse(response_builder.build())
				.build();
		
		session.writeToRemote(message.toByteArray());
	}
	
	public void groupSearching(IOSession session,String group_name) {
		Set<Group> groups = GroupCenter.Instance().searchGroupByName(group_name);
		groupSearchResponse(session,group_name,groups);
	}
	
	private void queryGroupsResponse(IOSession session,String email,Set<Group> groups) {
		GroupListResponse.Builder response_builder = GroupListResponse.newBuilder();
		response_builder.setUserEmail(email);

		groups.forEach(new Consumer<Group>() {
			@Override
			public void accept(Group group) {
				GroupInfo.Builder info_builder = GroupInfo.newBuilder();
				
				info_builder.setCreatorEmail(group.CreatorEmail())
				.setGroupId(group.ID())
				.setGroupIntro(group.Intro())
				.setGroupMemberNumber(group.MemberNum());
				
				ArrayList<Group.Member> members = group.Members();
				members.forEach(new Consumer<Group.Member>() {

					@Override
					public void accept(Member member) {
						GroupInfo.Member mem = GroupInfo.Member.newBuilder()
								.setMemberEmail(member.Email())
								.setMemberNickname(member.Nickname())
								.build();
						info_builder.addMembers(mem);
					}
					
				});
				
				response_builder.addGroups(info_builder.build());
			}
		});
		
		ProtoMessage message = ProtoMessage.newBuilder()
				.setGroupListResponse(response_builder.build())
				.build();
		
		session.writeToRemote(message.toByteArray());
	}
	
	public void queryGroups(IOSession session,String email) {
		Set<Group> groups = GroupCenter.Instance().getGroupsBy(email);
		queryGroupsResponse(session,email,groups);
	}
}





class GroupParticipateInRoutine{
	public enum Status{
		APPLYING,
		WAITTING_FOR_REPLY,
		FINISHED_WITH_SUCCESS,
		FINISHED_WITH_FAILURE,
		ERROR
	}
	private Status _status;
	private final String _applyer;
	private final Integer _group_id;
	public GroupParticipateInRoutine(String applyer,Integer group_id){
		this._applyer = applyer;
		this._group_id = group_id;
		this._status = Status.APPLYING;
	}
	
	@Override
	public boolean equals(Object routine) {
		return 
				((this._applyer == ((GroupParticipateInRoutine)routine)._applyer)
						&&
						( this._group_id== ((GroupParticipateInRoutine)routine)._group_id));
	}
	
	public GroupParticipateInRoutine NextStatus(GroupParticipateInApplyRequest request) {
		if(this.status()!= Status.APPLYING) {
			this._status = Status.ERROR;
			return this;
		}
		this._status = Status.WAITTING_FOR_REPLY;
		return this;
	}
	
	public GroupParticipateInRoutine NextStatus(GroupParticipateInReplyRequest request) {
		if(this.status()!= Status.WAITTING_FOR_REPLY) {
			this._status = Status.ERROR;
			return this;
		}
		
		boolean isAccepted = request.getIsAccepted();
		if(isAccepted)
			this._status = Status.FINISHED_WITH_SUCCESS;
		else
			this._status = Status.FINISHED_WITH_FAILURE;
		return this;
	}
	
	public Status status() {
		return this._status;
	}
	
	public String applyer() {
		return this._applyer;
	}
	
	public Integer GroupID() {
		return this._group_id;
	}
}
